﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
//using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;

using System.IO;

using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.Graphics;

namespace SBS_Sprite_Sheeter
{
    public partial class MainForm : Form
    {
        #region Structs
        struct OutputRectangle
        {
            public Rectangle rect;
            public Texture2D texture;
            public string name;

            public OutputRectangle(Rectangle rect, Texture2D tex, string name)
            {
                this.rect = rect;
                this.texture = tex;
                this.name = name;
            }
        }
        #endregion

        #region Variables

        #endregion

        #region Constructor
        public MainForm()
        {
            InitializeComponent();
        }
        #endregion

        private void load_Click(object sender, EventArgs e)
        {
            OpenFileDialog ofd = new OpenFileDialog();
            ofd.AddExtension = true;
            ofd.CheckFileExists = true;
            ofd.DefaultExt = "dds";
            ofd.InitialDirectory = System.IO.Directory.GetCurrentDirectory();
            ofd.Multiselect = true;
            ofd.Filter = "Image Files |*.bmp;*.dds;*.png;*.jpg;*.tga;*.tif;*.tiff|All Files|*.*";
            ofd.RestoreDirectory = false;

            if (ofd.ShowDialog() == DialogResult.OK)
            {
                spriteList.Items.Clear();
                spriteList.Items.AddRange(ofd.FileNames);
            }
        }

        private void createSheet_Click(object sender, EventArgs e)
        {
            #region Sprite Sheet Setup
            int spacing = 0;
            if (int.TryParse(spacingNum.Value.ToString(), out spacing) == false)
            {
                MessageBox.Show("Image Spacing is invalid");
                return;
            }
            if (spriteList.Items.Count == 0)
            {
                MessageBox.Show("No Images Selected");
                return;
            }

            if (alphaColor.Text.Length != 8)
            {
                MessageBox.Show("Invalid Fill Color, must be in Hex: ARGB (FF00FF00) is Green");
                return;
            }

            alphaColor.Text = alphaColor.Text.ToUpper();
            for (int x = 0; x < 8; x++)
            {
                switch (alphaColor.Text[x])
                {
                    case '0':
                    case '1':
                    case '2':
                    case '3':
                    case '4':
                    case '5':
                    case '6':
                    case '7':
                    case '8':
                    case '9':
                    case 'A':
                    case 'B':
                    case 'C':
                    case 'D':
                    case 'E':
                    case 'F':
                        break;
                    default:
                        {
                            MessageBox.Show("Invalid Fill Color, must be in Hex: ARGB (FF00FF00) is Green");

                            return;
                        }
                }
            }
            #endregion

            #region Sprite Sheet Maker
            GraphicsDevice graphicsDevice = CreateXNAGraphicsDevice();

            List<Texture2D> textures = new List<Texture2D>();
            List<string> textureNames = new List<string>();

            bool error = false;
            for (int x = 0; x < spriteList.Items.Count; x++)
            {
                string filename = (string)spriteList.Items[x];

                try
                {
                    Texture2D tex = Texture2D.FromFile(graphicsDevice, filename);
                    textures.Add(tex);
                    textureNames.Add(Path.GetFileNameWithoutExtension(filename));
                }
                catch (Exception err)
                {
                    error = true;
                    MessageBox.Show("Unable to load image: " + filename + " - " + err.Message);
                    break;
                }
            }

            if (!error)
            {
                int currentWidth = 64;
                int currentHeight = 64;

                List<OutputRectangle> outputRects = new List<OutputRectangle>();

                while (textures.Count > 0)
                {
                    int largestWidth = -1;
                    int largestIndex = -1;

                    Texture2D tex = null;

                    for (int x = 0; x < textures.Count; x++)
                    {
                        tex = textures[x];

                        if (tex.Width > largestWidth)
                        {
                            largestWidth = tex.Width;
                            largestIndex = x;
                        }
                    }

                    if (largestIndex == -1 || largestWidth == -1)
                    {
                        break;
                    }

                    tex = textures[largestIndex];
                    textures.RemoveAt(largestIndex);

                    string name = textureNames[largestIndex];
                    textureNames.RemoveAt(largestIndex);
                    if (outputRects.Count == 0)
                    {
                        outputRects.Add(new OutputRectangle(new Microsoft.Xna.Framework.Rectangle(
                            spacing, spacing, tex.Width, tex.Height), tex, name));

                        while (tex.Width + spacing > currentWidth)
                        {
                            currentWidth++;
                        }
                        while (tex.Height + spacing > currentHeight)
                        {
                            currentHeight++;
                        }
                    }
                    else
                    {
                        bool validRect = true;
                        bool canFit = false;

                        while (validRect)
                        {
                            canFit = false;

                            bool addedRect = false;
                            Microsoft.Xna.Framework.Rectangle outRect = new Microsoft.Xna.Framework.Rectangle();
                            bool hasOutRect = false;


                            for (int x = 0; x < outputRects.Count; x++)
                            {
                                OutputRectangle rect = outputRects[x];

                                Microsoft.Xna.Framework.Rectangle newRect =
                                    new Microsoft.Xna.Framework.Rectangle(
                                        rect.rect.Right + spacing,
                                        rect.rect.Top,
                                        tex.Width, tex.Height);

                                if (newRect.Right + spacing <= currentWidth &&
                                    newRect.Bottom + spacing <= currentHeight)
                                {
                                    canFit = true;

                                    for (int y = 0; y < outputRects.Count; y++)
                                    {
                                        if (y == x)
                                            continue;

                                        OutputRectangle testRect = outputRects[y];

                                        if (testRect.rect.Intersects(newRect))
                                        {
                                            validRect = false;
                                            break;
                                        }

                                    }

                                    if (validRect)
                                    {
                                        if (hasOutRect == false ||
                                            ((new Vector2(newRect.Top, newRect.Left)).Length() <
                                             (new Vector2(outRect.Top, outRect.Left)).Length()))
                                        {
                                            outRect = newRect;
                                            hasOutRect = true;
                                        }
                                    }
                                }

                                validRect = true;
                                newRect =
                                        new Microsoft.Xna.Framework.Rectangle(
                                            rect.rect.Left, rect.rect.Bottom + spacing, tex.Width, tex.Height);

                                canFit = false;

                                if (newRect.Right + spacing <= currentWidth && newRect.Bottom + spacing <= currentHeight)
                                {
                                    canFit = true;

                                    for (int y = 0; y < outputRects.Count; y++)
                                    {
                                        if (y == x)
                                            continue;

                                        OutputRectangle testRect = outputRects[y];

                                        if (testRect.rect.Intersects(newRect))
                                        {
                                            validRect = false;
                                            break;
                                        }
                                    }

                                    if (validRect)
                                    {
                                        if (hasOutRect == false ||
                                            ((new Vector2(newRect.Top, newRect.Left)).Length() <
                                             (new Vector2(outRect.Top, outRect.Left)).Length()))
                                        {
                                            outRect = newRect;
                                            hasOutRect = true;
                                        }
                                    }
                                }
                            }


                            if (hasOutRect == true)
                            {
                                outputRects.Add(new OutputRectangle(outRect, tex, name));
                                addedRect = true;
                            }

                            if (addedRect == false)
                            {
                                if (canFit == false)
                                {
                                    currentWidth++;
                                    currentHeight++;
                                }
                                validRect = true;
                            }
                            else
                            {
                                break;
                            }
                        }
                    }
                }

                WriteOutput(graphicsDevice, currentWidth, currentHeight, outputRects);
            }

            graphicsDevice.Dispose();
            #endregion
        }

        private void WriteOutput(GraphicsDevice graphicsDevice, int currentWidth, int currentHeight, List<OutputRectangle> outputRects)
        {
            Texture2D newTex = new Texture2D(graphicsDevice, currentWidth, currentHeight, 1, TextureUsage.AutoGenerateMipMap, SurfaceFormat.Color);

            Color color = new Color(byte.Parse(alphaColor.Text.Substring(2, 2), System.Globalization.NumberStyles.HexNumber),
                                    byte.Parse(alphaColor.Text.Substring(4, 2), System.Globalization.NumberStyles.HexNumber),
                                    byte.Parse(alphaColor.Text.Substring(6, 2), System.Globalization.NumberStyles.HexNumber),
                                    byte.Parse(alphaColor.Text.Substring(0, 2), System.Globalization.NumberStyles.HexNumber));

            Color[] newColor = new Color[currentWidth * currentHeight];

            for (int x = 0; x < currentWidth*currentHeight; x++)
            {
                newColor[x] = color;
            }

            for (int j = 0; j < outputRects.Count; j++)
            {
                OutputRectangle rect = outputRects[j];
                Color[] texColor = new Color[rect.texture.Width * rect.texture.Height];

                rect.texture.GetData<Color>(texColor);

                for (int x = 0; x < rect.rect.Width; x++)
                {
                    for (int y = 0; y < rect.rect.Height; y++)
                    {
                        newColor[rect.rect.Left + x + (rect.rect.Top + y) * currentWidth] = texColor[x + y * rect.texture.Width];
                    }
                }
            }

            newTex.SetData<Color>(newColor);

            try
            {
                newTex.Save(outDir.Text.Replace(".txt", "") + "PNG.png", ImageFileFormat.Png);
            }
            catch (Exception err)
            {
                MessageBox.Show("Unable to save texture file: " + err.Message);
            }

            SpriteSheet ptex = new SpriteSheet();
            ptex.texture = newTex;
            for (int j = 0; j < outputRects.Count; j++)
            {
                OutputRectangle rect = outputRects[j];
                ptex.sprites.Add(rect.name, new SpriteElement(rect.rect, rect.name));
            }

            try
            {
                ptex.Save(outDir.Text);
            }
            catch (Exception err)
            {
                MessageBox.Show("Unable to save xml file: " + err.Message);
            }
        }

        private GraphicsDevice CreateXNAGraphicsDevice()
        {
            GraphicsDevice graphicsDevice;
            PresentationParameters presentParams = new PresentationParameters();
            presentParams.BackBufferHeight = 512;
            presentParams.BackBufferWidth = 512;
            presentParams.BackBufferFormat = Microsoft.Xna.Framework.Graphics.SurfaceFormat.Color;
            presentParams.BackBufferCount = 1;
            presentParams.DeviceWindowHandle = this.Handle;
            presentParams.EnableAutoDepthStencil = true;
            presentParams.AutoDepthStencilFormat = DepthFormat.Unknown;
            presentParams.IsFullScreen = false;
            presentParams.MultiSampleType = MultiSampleType.None;
            presentParams.PresentationInterval = PresentInterval.Immediate;
            presentParams.PresentOptions = PresentOptions.None;
            presentParams.SwapEffect = SwapEffect.Discard;

            graphicsDevice = new Microsoft.Xna.Framework.Graphics.GraphicsDevice(GraphicsAdapter.DefaultAdapter,
                DeviceType.Hardware, this.Handle, new PresentationParameters());
            return graphicsDevice;
        }

        private void directory_Click(object sender, EventArgs e)
        {
            OpenFileDialog ofd = new OpenFileDialog();
            ofd.AddExtension = true;
            ofd.CheckFileExists = false;
            ofd.DefaultExt = "xml";
            ofd.InitialDirectory = System.IO.Directory.GetCurrentDirectory();
            ofd.Multiselect = false;
            ofd.Filter = "TXT Files (*.txt)|*.txt";
            ofd.RestoreDirectory = false;

            if (ofd.ShowDialog() == DialogResult.OK)
            {
                outDir.Text = ofd.FileName;
            }
        }
    }
}